package dbexp.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * Klasa sluzy do zapytania rejestru systemu windows o sterownik ODBC
 * @author Michal Skrzynski
 *
 */
public class WindowsRegQueryODBC {
	/** Komenda ktora zostanie uzyta do wykonania zapytania na rejestrze */
	private static final String REGQUERY_UTIL = "reg query ";
	/** Glowny klucz rejestru do sprawdzenia */
	private static final String REG_MAIN_KEY = "HKCU";
	/** Podklucz ze sterownikami ODBC */
	private static final String REG_FOLDER = "\\SOFTWARE\\ODBC\\ODBC.INI";
	/** Laczenie komendy w calosc */
	private static final String CMD_COMMAND = REGQUERY_UTIL +
	REG_MAIN_KEY+REG_FOLDER + " /s";// /s kwerenda o wszystkie podklucze i wartości
	/** Token  opisu */
	private static final String DESCRIPTION_TOKEN = "Description\tREG_SZ\t";
	/** Token nazwy serwera */
	private static final String SERVERNAME_TOKEN = "ServerName\tREG_SZ\t";
	/** Token nazwe zrodla danych */
	private static final String DSN_TOKEN = "DSN\tREG_SZ\t";
	/** lista sterownikow ODBC */
	private static List<ODBC> odbcs = new ArrayList<ODBC>();
	/** zmienna pomocnicza do jednokrotnego sprawdzania rejestru */
	private static boolean init = false;
	/** okresla czy w systemie znajduja sie sterowniki ODBC */
	private static boolean hasODBCDriver = false;
	
	/**
	 * Sprawdza czy system Windows posiada sterowniki ODBC
	 * @return hasODBCDriver
	 */
	public static boolean hasODBCDriver(){
		if (System.getProperty("os.name","unknown").startsWith("Windows")) {
			boolean has = hasODBCDriver;
			if(!has){
				if(!init){
					checkODBCSystemDSNs();
				}
				has=odbcs.size()>0;
			}
			return has;
		}else{
			return false;
		}
	}
	/**
	 * Pobiera sterownik ODBC o okreslonym indeksie lub pusta definicje sterownika
	 * @param idx numer sterownika
	 * @return obiekt z opisem sterownika ODBC
	 */
	public static ODBC getODBC(int idx){
		if(idx > 0 || idx < odbcs.size()){
			return odbcs.get(idx);
		}
		return new ODBC();
	}
	public static void main(String[] args) {
		if(hasODBCDriver()){
			for (ODBC odbc : odbcs) {
				System.out.println("Description: "+odbc.getDescription());
				System.out.println("ServerName: "+odbc.getServerName());
				System.out.println("DSN: "+odbc.getDSN());
			}
		}
	}
	
	/**
	 * Metoda sprawdzajaca czy w rejestrze systemu Windows sa zapisane jakies sterowniki ODBC.</br>
	 * Jezeli zostaly jakies znalezione, to sa ladowane
	 * @return czy system zawiera sterownik ODBC
	 */
	public static boolean checkODBCSystemDSNs() {
		boolean out = false;
		String os = System.getProperty("os.name");
		if (os.startsWith("Windows")) {
			try {
				Process process = Runtime.getRuntime().exec(CMD_COMMAND);
				StreamReader reader = new StreamReader(process.getInputStream());
	
				reader.start();
				process.waitFor();
				reader.join();
	
				String result = reader.getResult(); // wyciągamy wyniki komendy
				result = substring(result, "ODBC Data Sources");
				result = substring(result, "HKEY_CURRENT_USER");
				while(result.contains(REG_FOLDER)){
					result = result.substring(result.indexOf(REG_FOLDER)); // bierzemy
																				// tylko
																				// dane
																				// z
																				// konkretnych
																				// sterowników
																				// ODBC
		
					result = substring(result, DESCRIPTION_TOKEN); // usuwamy wszytsko
																	// do
																	// DESCRIPTION_TOKEN
					String description = getString(result, DESCRIPTION_TOKEN); // pobieramy
																		// DESCRIPTION_TOKEN
		
					result = substring(result, SERVERNAME_TOKEN); // usuwamy wszytsko do
																	// SERVERNAME_TOKEN
					String serverName = getString(result, SERVERNAME_TOKEN); // pobieramy
																		// SERVERNAME_TOKEN
		
					result = substring(result, DSN_TOKEN); // usuwamy wszytsko do
															// DSN_TOKEN
					String dsn = getString(result, DSN_TOKEN); // pobieramy DSN_TOKEN
					ODBC odbc = new ODBC(description, serverName, dsn);
					if(odbc.isValidODBCDriver()){
						odbcs.add(odbc);
					}
				}
				
				hasODBCDriver = odbcs.size()>0;
			} catch (Exception e) {
				out = false;
			}
		}
		init = true;
		return out;
	}
	/**
	 * Metoda do obcinania ciagu znakowego od momentu wystapienie ciagu <code>what</code>
	 * @param result ciag wejsciowy
	 * @param what ciag szukany
	 * @return result.substring(what)
	 */
	private static String substring(String result, String what){
		int idx_what = result.indexOf(what);
		if(result!=null && result.trim().length()>0 && idx_what!=-1){
			return result.substring(idx_what);
		}
		return result;
	}
	/**
	 * Metoda do pobierania wartosci z ciagu wejciowego <code>result</code> o kluczu na ktory wskazuje <code>what</code>
	 * @param result ciag wejsciowy
	 * @param what klucz wartosci
	 * @return wartosc
	 */
	private static String getString(String result, String what){
		int idx_what = result.indexOf(what);
		if(result!=null && result.trim().length()>0 && idx_what!=-1){
			int idx_end = result.indexOf("\r\n");
			return result.substring(idx_what+what.length(), idx_end);
		}
		return null;
	}
	public static class ODBC{
		/** Opis sterownika */
		private String description = null;
		/** Nazwa serwera */
		private String serverName = null;
		/** Data Source Name */
		private String dsn = null;
		
		public ODBC(){
			
		}
		public ODBC(String description, String serverName, String dsn){
			this.description = description;
			this.serverName = serverName;
			this.dsn = dsn;
		}
		/**
		 * Sprawdza czy sterownik ma prawidlowe dane
		 * @return sterownika ma prawidlowe dane
		 */
		public boolean isValidODBCDriver(){
			return this.description!=null &&this.dsn!=null && this.serverName!=null;
		}

		/**
		 * Pobiera opis sterownika ODBC
		 * @return description
		 */
		public String getDescription() {
			return description;
		}

		/**
		 * Pobiera nazwe serwera
		 * @return serverName
		 */
		public String getServerName() {
			return serverName;
		}

		/**
		 * Pobiera nazwe steronika danych
		 * @return dsn
		 */
		public String getDSN() {
			return dsn;
		}
	}
	static class StreamReader extends Thread {
	  private InputStream is;
	  private StringWriter sw;

	  StreamReader(InputStream is) {
		this.is = is;
		sw = new StringWriter();
	  }

	  public void run() {
		try {
		  int c;
		  while ((c = is.read()) != -1)
			sw.write(c);
		  }
		  catch (IOException e) { ; }
		}

	  String getResult() {
		return sw.toString();
	  }
	}
}
